package com.example.user.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.common.core.domain.R;
import com.example.common.core.enums.ResultCode;
import com.example.common.security.exception.ServiceException;
import com.example.user.domain.course.Course;
import com.example.user.domain.video.Video;
import com.example.user.domain.video.dto.VideoQueryDTO;
import com.example.user.domain.video.vo.VideoVO;
import com.example.user.mapper.course.CourseMapper;
import com.example.user.mapper.video.VideoMapper;
import com.example.user.service.IVideoService;
import com.github.pagehelper.PageHelper;
import jakarta.servlet.http.HttpServletResponse;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FrameGrabber;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.nio.charset.StandardCharsets;

@Service
@Transactional(readOnly = true)
public class VideoServiceImpl implements IVideoService {

    @Autowired
    private VideoMapper videoMapper;

    @Autowired
    private CourseMapper courseMapper;

    private static final String COURSE_DIR = Paths.get(System.getProperty("user.dir")).toString();

    @Override
    public List<VideoVO> list(VideoQueryDTO videoQueryDTO) {
        PageHelper.startPage(videoQueryDTO.getPageNum(), videoQueryDTO.getPageSize());
        return videoMapper.selectListByCourseId(videoQueryDTO.getCourseId());

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseEntity<R<String>> upload(MultipartFile videoFile, Long courseId) {

        Course course = courseMapper.selectById(courseId);
        if (course == null) {
            throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        }

        String relativePath = "/uploads/course/"+courseId;
        File uploadDir = new File(COURSE_DIR+relativePath);
        if (!uploadDir.exists()) {
            uploadDir.mkdirs();
        }
        String fileName = videoFile.getOriginalFilename();
        try {
            if(fileName.contains("/")){
                throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
            }
            List<Video> videoLst = videoMapper.selectList(new LambdaQueryWrapper<Video>()
                    .eq(Video::getVideoName, fileName)
                    .eq(Video::getCourseId,courseId));
            if(CollectionUtil.isNotEmpty(videoLst)){
                throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
            }
            File targetFile = new File(uploadDir, fileName);
            if (targetFile.exists()) {
                throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
            }
            videoFile.transferTo(targetFile); // 重名文件不会覆盖
            // 获取视频时长（网页1 FFmpeg方案）
            Double duration = getVideoDuration(targetFile);
            Video video = new Video();
            video.setCourseId(courseId);
            video.setVideoName(fileName);
            video.setVideoDuration(duration);
            video.setPath(relativePath+"/");
            videoMapper.insert(video);
            
            // 添加视频并更新课程
            course.addVideo();
            
            // 如果是第一个视频，且课程状态为已创建，则将状态更改为录制中
            if (course.getVideoNum() == 1 && Course.STATUS_CREATED == course.getStatus()) {
                course.setStatus(Course.STATUS_RECORDING);
            }
            
            courseMapper.updateById(course);
            return ResponseEntity.ok(R.ok());
        } catch (IOException e) {
            return ResponseEntity.status(500).body(R.fail());
        } catch (ServiceException e){
            return ResponseEntity.status(500).body(R.fail(ResultCode.FAILED_ILLEGAL_FILE_NAME.getCode(),fileName+" 与已有文件名冲突"));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int delete(Long videoId) {
        Video video = videoMapper.selectById(videoId);

        if (video == null) {
            throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        }
        Course course = courseMapper.selectById(video.getCourseId());
        if (course == null) {
            throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        }
        try {
            Path source = Paths.get(COURSE_DIR, video.getPath(), video.getVideoName());
            Files.delete(source);
            course.deleteVideo();
            courseMapper.updateById(course);
            return videoMapper.deleteById(videoId);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int reName(Long videoId,String newName) {
        if(newName.contains("/")){
            throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        }
        Video video = videoMapper.selectById(videoId);
        if (video == null) {
            throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        }
        String oldName = video.getVideoName();
        Path source = Paths.get(COURSE_DIR, video.getPath(),oldName);
        Path target = Paths.get(COURSE_DIR, video.getPath(),newName);
        try {
            if (Files.exists(target)) {
                throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
            }
            Files.move(source, target);
            video.setVideoName(newName);
            return videoMapper.updateById(video);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ServiceException(ResultCode.FAILED);
        }

    }

    @Override
    public VideoVO get(Long videoId) {
        Video video = videoMapper.selectById(videoId);
        if (video == null) {
            throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        }
        VideoVO videoVO = new VideoVO();
        BeanUtil.copyProperties(video, videoVO);
        return videoVO;
    }

    @Override
    public void streamVideo(Long videoId, HttpServletResponse response) {
        // 根据视频 ID 获取视频文件路径
        Video video = videoMapper.selectById(videoId);
        if (video == null) {
            throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        }
        File videoFile = Paths.get(COURSE_DIR, video.getPath(),video.getVideoName()).toFile();

        response.setContentType("video/mp4");
        // 原句
        // response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + URLEncoder.encode(videoFile.getName()) + "\"");


        // 清理文件名，移除路径分隔符和特殊字符
        String safeFileName = videoFile.getName().replaceAll("[\\\\/:*?\"<>|]", "_");
        // 限制文件名长度
        safeFileName = safeFileName.substring(0, Math.min(safeFileName.length(), 255));
        // 使用UTF-8字符集进行编码
        String encodedFileName = URLEncoder.encode(safeFileName, StandardCharsets.UTF_8);
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + encodedFileName + "\"");
        // 设置响应流
        try (InputStream videoStream = new FileInputStream(videoFile); OutputStream outStream = response.getOutputStream()) {
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = videoStream.read(buffer)) != -1) {
                outStream.write(buffer, 0, bytesRead);
            }
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private double getVideoDuration(File videoFile) throws FrameGrabber.Exception {
        try (FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(videoFile)) {
            grabber.start();
            return grabber.getLengthInTime() / 1000000.0; // 转换为秒
        }
    }

}
